#include "NoiseMap.h"
#include "Curve/hsv.h"

NoiseMap::NoiseMap()
{

    EffectDetails.EffectName = "NoiseMap";
    //EffectDetails.EffectClassName = ClassName();
    EffectDetails.EffectDescription = "Floor is lava";
    EffectDetails.MaxSpeed     = 100;
    EffectDetails.MinSpeed     = 1;
    EffectDetails.HasCustomSettings = true;

//    ui->colors_choice->addItems({"Rainbow", "Inverse rainbow", "Custom"});
//    ui->colors_frame->hide();

//    ui->motion->addItems({"Up", "Down", "Left", "Right"});

//    ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//    ui->colors->setLayout(new QHBoxLayout());
//    ui->colors->layout()->setSizeConstraint(QLayout::SetFixedSize);
//    ui->scrollArea->setWidgetResizable(true);

    SetSpeed(50);
    Defaults();
    ResetNoise();
    ResetColors();
}
NoiseMap::~NoiseMap()
{
    delete noise;
}
void NoiseMap::ResetColors()
{
//    QLayoutItem *child;

//    while ((child = ui->colors->layout()->takeAt(0)) != 0) {
//        delete child->widget();
//    }

//    unsigned int colors_count = ui->colors_count_spinBox->value();

//    color_pickers.resize(colors_count);
//    colors.resize(colors_count);

//    for(unsigned int i = 0; i < colors_count; i++)
//    {
//        ColorPicker* picker = CreatePicker(i);
//        ui->colors->layout()->addWidget(picker);
//    }

    GenerateGradient();
}

void NoiseMap::GenerateGradient()
{
//    QGradientStops stops;

//    unsigned int colors_count = colors.size();

//    float color_step = 1.f / colors_count;

//    for(unsigned int i = 0; i < colors_count; i++)
//    {
//        QGradientStop stop = QGradientStop(
//                    i * color_step,
//                    QColor(RGBGetRValue(colors[i]), RGBGetGValue(colors[i]), RGBGetBValue(colors[i]))
//                    );

//        stops << stop;
//    }

//    QGradient::Spread spread = QGradient::PadSpread;

//    QLinearGradient grad(QPointF(0,0), QPointF(100,0));
//    grad.setSpread(spread);
//    grad.setStops(stops);

//    QBrush brush = QBrush(grad);
//    QRectF rect(0, 0, 100, 1);
//    QPainter painter(&image);
//    painter.fillRect(rect, brush);
}

//ColorPicker* NoiseMap::CreatePicker(int i)
//{
//    ColorPicker* picker = new ColorPicker();
//    picker->SetRGBColor(colors[i]);

//    color_pickers[i] = picker;

//    connect(picker, &ColorPicker::ColorSelected, [=](QColor c){
//        colors[i] = ColorUtils::fromQColor(c);
//        GenerateGradient();
//    });

//    return picker;
//}

void NoiseMap::Defaults()
{
//    ui->amplitude->setValue(default_amplitude * val_mult);
//    ui->frequency->setValue(default_frequency * val_mult);
//    ui->lacunarity->setValue(default_lacunarity * val_mult);
//    ui->persistence->setValue(default_persistence * val_mult);
//    ui->octaves->setValue(default_octaves);
}

void NoiseMap::ResetNoise()
{
    noise = new SimplexNoise(frequency, amplitude, lacunarity, persistence);
}

//void NoiseMap::DefineExtraOptions(QLayout* layout)
//{
//    layout->addWidget(this);
//}

void NoiseMap::StepEffect(ControllerZone* const controller_zone)
{
    //for(ControllerZone* controller_zone: controller_zones)
    {
        int start_idx = controller_zone->start_idx();
        zone_type ZT = controller_zone->type();
        int leds_count = controller_zone->leds_count();

        if (ZT == ZONE_TYPE_SINGLE || ZT == ZONE_TYPE_LINEAR)
        {
            for (int LedID = 0; LedID < leds_count; LedID++)
            {
                RGBColor color = GetColor(LedID, 0);
                controller_zone->SetLED(start_idx + LedID, color, Brightness);
            }
        }

        else if (ZT == ZONE_TYPE_MATRIX)
        {
            int cols = controller_zone->matrix_map_width();
            int rows = controller_zone->matrix_map_height();

            for (int col_id = 0; col_id < cols; col_id++)
            {
                for (int row_id = 0; row_id < rows; row_id++)
                {
                    RGBColor color = GetColor(col_id, row_id);
                    //int LedID = controller_zone->map[((row_id * cols) + col_id)];
                    int LedID = (row_id * cols) + col_id;
                    controller_zone->SetLED(start_idx + LedID, color, Brightness);
                }
            }
        }
    }

    progress +=  0.1 * (float) Speed / (float) FPS;
}

RGBColor NoiseMap::GetColor(unsigned int x, unsigned int y)
{
    float x_shift = 0;
    float y_shift = 0;

    switch (motion) {

    case 0: y_shift = motion_speed * progress; break;
    case 1: y_shift = motion_speed * -progress;  break;
    case 2: x_shift = motion_speed * progress;  break;
    case 3: x_shift = motion_speed * -progress;  break;

    default: break;
    }


    float value = noise->fractal(octaves, x + x_shift, y + y_shift, progress);
    float frac = (1+value)*0.5;

    int color_choice = 1;//ui->colors_choice->currentIndex();
    hsv_t hsv;

    switch (color_choice)
    {
    case 0:
        hsv.hue = 360 * frac;
        hsv.saturation = 255;
        hsv.value = 255 ;
        return RGBColor(hsv2rgb(&hsv));

    case 1:
        hsv.hue = 360 - 360 * frac;
        hsv.saturation = 255;
        hsv.value = 255 ;
        return RGBColor(hsv2rgb(&hsv));

//    case 2:
//        int color_x = (1 - frac) * 100;
//        QColor c = image.pixelColor(color_x, 0);
//        return ColorUtils::fromQColor(c);
    }

    //return ColorUtils::OFF();
    return 0x00;
}
#if 0
void NoiseMap::LoadCustomSettings(json settings)
{
    if(settings.contains("frequency"))  frequency  = settings["frequency"];
    if(settings.contains("amplitude"))  amplitude  = settings["amplitude"];
    if(settings.contains("lacunarity"))  lacunarity  = settings["lacunarity"];
    if(settings.contains("persistence"))  persistence  = settings["persistence"];
    if(settings.contains("octaves"))  octaves  = settings["octaves"];
    if(settings.contains("motion"))   motion  = settings["motion"];
    if(settings.contains("motion_speed"))  motion_speed  = settings["motion_speed"];

    ui->amplitude->setValue(amplitude * val_mult);
    ui->frequency->setValue(frequency * val_mult);
    ui->lacunarity->setValue(lacunarity * val_mult);
    ui->persistence->setValue(persistence * val_mult);
    ui->octaves->setValue(octaves);
    ui->motion_speed->setValue(motion_speed);
    ui->motion->setCurrentIndex(motion);


    if (settings.contains("colors_choice"))
    {
        int colors_choice = settings["colors_choice"];
        ui->colors_choice->setCurrentIndex(colors_choice);
    }

    if (settings.contains("colors"))
    {
        colors.clear();

        for(unsigned int color : settings["colors"])
        {
            colors.push_back(color);
        }

        ui->colors_count_spinBox->setValue(colors.size());
    }

    ResetColors();
}

json NoiseMap::SaveCustomSettings(json settings)
{
    settings["frequency"] = frequency;
    settings["amplitude"] = amplitude;
    settings["lacunarity"] = lacunarity;
    settings["persistence"] = persistence;
    settings["octaves"] = octaves;

    settings["colors"] = colors;
    settings["colors_choice"] = ui->colors_choice->currentIndex();

    settings["motion"] = motion;
    settings["motion_speed"] = motion_speed;

    return settings;
}

void NoiseMap::on_amplitude_valueChanged(int value)
{
    amplitude = value * inv_val_mult;
    ResetNoise();
}

void NoiseMap::on_frequency_valueChanged(int value)
{
    frequency = value * inv_val_mult;
    ResetNoise();
}

void NoiseMap::on_lacunarity_valueChanged(int value)
{
    lacunarity = value * inv_val_mult;
    ResetNoise();
}

void NoiseMap::on_persistence_valueChanged(int value)
{
    persistence = value * inv_val_mult;
    ResetNoise();
}

void NoiseMap::on_octaves_valueChanged(int value)
{
    octaves = value;
    ResetNoise();
}

void NoiseMap::on_motion_speed_valueChanged(int value)
{
    motion_speed = value;
}

void NoiseMap::on_motion_currentIndexChanged(int value)
{
    motion = value;
}

void NoiseMap::on_defaults_clicked()
{
    Defaults();
}

void NoiseMap::on_colors_choice_currentIndexChanged(int value)
{
    ui->colors_frame->setVisible(value==2);
}

void NoiseMap::on_colors_count_spinBox_valueChanged(int)
{
    ResetColors();
}
#endif
